﻿using DB2WORD.Logics;
using DB2WORD.Models;
using Newtonsoft.Json;
using System.Data;
using System.Diagnostics;
using System.Text;
using System.Text.RegularExpressions;

namespace DB2WORD
{
    public partial class FrmEdit : Form
    {
        DragableTree dt;
        public FrmEdit()
        {
            InitializeComponent();
            this.Shown += this.FrmEdit_Load;
            this.SetDoubleBuffer();
            this.tvTables.SetDoubleBuffer();
            this.dgvColumns.SetDoubleBuffer();
            this.btnAi.Enabled = false;
            this.btnSave.Enabled = false;
            this.btnCancel.Enabled = false;
            this.pnlSelect.Enabled = false;
            this.pnlOper.Enabled = false;
            this.btnPosition.Enabled = false;
            this.pnlTableInfo.Visible = false;
        }

        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams cp = base.CreateParams;
                cp.ExStyle |= 0x02000000;
                return cp;
            }
        }

        private async void FrmEdit_Load(object? sender, EventArgs e)
        {
            this.btnExpand.Click += async (_, _) => { tvTables.ExpandAll(); this.btnExpand.Delay(); };
            this.btnCollapse.Click += async (_, _) => { tvTables.CollapseAll(); this.btnCollapse.Delay(); };
            this.btnPosition.Click += this.BtnPosition_Click;
            this.btnFind.Click += this.BtnFind_Click;
            this.btnIgnoreCase.Click += this.BtnIgnoreCase_Click;
            this.tvTables.NodeMouseDoubleClick += this.tvTables_NodeMouseDoubleClick;
            this.btnCancel.Click += this.BtnCancel_Click;
            this.btnSave.Click += this.BtnSave_Click;
            this.dgvColumns.CellBeginEdit += this.DgvColumns_CellBeginEdit;
            this.txtLabel.TextChanged += this.TxtLabel_TextChanged;
            this.txtDescription.TextChanged += this.TxtLabel_TextChanged;
            this.btnAi.Click += this.BtnAi_Click;
            this.dgvColumns.AutoGenerateColumns = false;
            this.btnScript.Click += this.BtnScript_Click;
            this.btnCreate.Click += this.btnCreate_Click;
            this.btnAiGlobal.Click += async (_, _) => MainForm.Self.More();
            this.btnCopyTableName.Click += this.BtnCopyTableName_Click;
            this.btnNewGroup.Click += this.BtnNewGroup_Click;
            this.txtRegex.GotFocus += (_, _) => this.txtRegex.SelectAll();
            this.txtRegex.MouseClick += (_, _) => this.txtRegex.SelectAll();
            this.tvTables.AfterSelect += this.TvTables_AfterSelect;
            this.btnDeleteGroup.Click += this.BtnDeleteGroup_Click;

            this.dt = new DragableTree(this.tvTables);
            dt.BeforeDrop = (node, action) =>
            {
                if(node.Tag is TableInfo tbl && node.Parent.Tag is GroupInfo g)
                {
                    g.Tables.Remove(tbl);
                    node.Parent.Text = $"{g.Name} ({g.Tables.Count})";
                }
            };
            dt.AfterDrop = (node, action) =>
            {
                if(node.Level == 0)
                {
                    MainForm.Groups.Clear();
                    MainForm.Groups.AddRange(this.tvTables.Nodes.OfType<TreeNode>().Select(x => x.Tag as GroupInfo));
                }
                else
                {
                    if(node.Parent.Tag is GroupInfo g)
                    {
                        g.Tables = node.Parent.Nodes.OfType<TreeNode>().Select(x => x.Tag as TableInfo).ToList();
                        node.Parent.Text = $"{g.Name} ({g.Tables.Count})";
                    }
                }
                this.Save();
            };
            this.SetTooltip();
        }

        private void BtnDeleteGroup_Click(object? sender, EventArgs e)
        {
            if(this.tvTables.SelectedNode?.Tag is GroupInfo g && g.Tables.Count == 0)
            {
                this.tvTables.Nodes.Remove(this.tvTables.SelectedNode);
                MainForm.Groups.Clear();
                MainForm.Groups.AddRange(this.tvTables.Nodes.OfType<TreeNode>().Select(x => x.Tag as GroupInfo));
                this.Save();
            }
        }

        private void TvTables_AfterSelect(object? sender, TreeViewEventArgs e)
        {
            if(e.Node.Level == 0 && e.Node.Tag is GroupInfo g && g.Tables.Count == 0)
            {
                this.btnDeleteGroup.Enabled = true;
            }
            else
            {
                this.btnDeleteGroup.Enabled = false;
            }
        }

        private async void BtnCopyTableName_Click(object? sender, EventArgs e)
        {
            var btnName = this.btnCopyTableName.Text;
            this.btnCopyTableName.Enabled = false;
            this.btnCopyTableName.Text = "复制成功";
            Clipboard.SetDataObject(this.lastTable.Name);
            await this.btnCopyTableName.Delay(action: () => { this.btnCopyTableName.Text = "复制表名称"; });
        }

        private void SetTooltip()
        {
            MainForm.Self.toolTip.SetToolTip(this.btnExpand, "展开所有");
            MainForm.Self.toolTip.SetToolTip(this.btnCollapse, "折叠所有");
            MainForm.Self.toolTip.SetToolTip(this.btnPosition, "定位到当前表名称");
            MainForm.Self.toolTip.SetToolTip(this.btnFind, "根据正则表达式查找");
            MainForm.Self.toolTip.SetToolTip(this.btnIgnoreCase, "忽略大小写");
            MainForm.Self.toolTip.SetToolTip(this.btnNewGroup, "新建分组");
            MainForm.Self.toolTip.SetToolTip(this.btnAi, "使用AI智能分析字段名称");
        }

        private bool isCaseSensitive = false;
        private TableInfo? lastTable = null;
        private GroupInfo? lastGroup = null;
        private TreeNode? lastNode = null;
        private bool isTableModified = false;
        private bool isGroupModified = false;

        public async void ShowFrom()
        {
            this.isCaseSensitive = false;
            this.btnIgnoreCase.BackColor = SystemColors.Control;
            this.txtNewGroupName.Enabled = this.btnNewGroup.Enabled = false;
            this.btnDeleteGroup.Enabled = false;

            Application.DoEvents();
            await Task.Run(() => this.Invoke(() => this.ShowTree()));
        }

        private void Clear()
        {
            this.tvTables.Nodes.Clear();
            this.dgvColumns.DataSource = null;
        }

        private async Task ShowTree()
        {
            Application.DoEvents();
            this.tvTables.Nodes.Clear();
            Application.DoEvents();
            foreach(var grp in MainForm.Groups)
            {
                var nodeGrp = new TreeNode($"{grp.Name} ({grp.Tables.Count})");
                nodeGrp.Tag = grp;
                this.tvTables.Nodes.Add(nodeGrp);
                foreach(var tbl in grp.Tables)
                {
                    var nodeTbl = new TreeNode(tbl.Name);
                    nodeTbl.Tag = tbl;
                    nodeGrp.Nodes.Add(nodeTbl);
                }
                Application.DoEvents();
            }
            this.tvTables.Invalidate();
            this.pnlSelect.Enabled = this.tvTables.Nodes.Count > 0;
            this.pnlOper.Enabled = this.tvTables.Nodes.Count > 0;
            this.txtNewGroupName.Enabled = this.btnNewGroup.Enabled = true;
            var gc = MainForm.Groups.Count();
            var tc = MainForm.Groups.Sum(x => x.Tables.Count());
            MainForm.Self.SetMessage($"{gc} 个分组，共 {tc} 个表格加载完成");
            Application.DoEvents();
        }

        private async void BtnPosition_Click(object? sender, EventArgs e)
        {
            btnPosition.Enabled = false;
            if(this.lastTable == null)
            {
                if(this.tvTables.SelectedNode != null)
                {
                    this.tvTables.SelectedNode.EnsureVisible();
                    this.tvTables.Select();
                }
                btnPosition.Enabled = false;
                return;
            }
            var tableName = this.lastTable.Name;
            var nodes = tvTables.Nodes.OfType<TreeNode>().SelectMany(x => x.Nodes.OfType<TreeNode>()).ToList();
            foreach(TreeNode node in nodes)
            {
                if(node.Tag is TableInfo tbl && tbl.Name.Equals(tableName, StringComparison.OrdinalIgnoreCase))
                {
                    node.EnsureVisible();
                    this.tvTables.SelectedNode = node;
                    this.tvTables.Select();
                    Application.DoEvents();
                    break;
                }
                Application.DoEvents();
            }
            await this.btnPosition.Delay();
        }

        private int lastFind = -1;
        private async void BtnFind_Click(object? sender, EventArgs e)
        {
            var nodes = this.tvTables.Nodes.OfType<TreeNode>().SelectMany(x => x.Nodes.OfType<TreeNode>()).ToList();
            var regexOptions = RegexOptions.Compiled;
            if(!this.isCaseSensitive)
            {
                regexOptions |= RegexOptions.IgnoreCase;
            }
            var regex = new Regex(txtRegex.Text.Trim(), regexOptions);
            var start = this.lastFind;
            for(int i = start + 1; i <= nodes.Count + start; i++)
            {
                var index = i % nodes.Count;
                var node = nodes[index];
                var tbl = node.Tag as TableInfo;
                if(regex.IsMatch(tbl.Name))
                {
                    this.lastFind = index;
                    node.EnsureVisible();
                    this.tvTables.SelectedNode = node;
                    this.tvTables.Select();
                    Application.DoEvents();
                    return;
                }
            }
            await this.btnFind.Delay();
        }
        private void BtnIgnoreCase_Click(object? sender, EventArgs e)
        {
            this.isCaseSensitive = !this.isCaseSensitive;
            this.btnIgnoreCase.BackColor = this.isCaseSensitive ? Color.Bisque : SystemColors.Control;
        }

        private void DgvColumns_CellBeginEdit(object? sender, DataGridViewCellCancelEventArgs e)
        {
            if(!this.isTableModified)
            {
                this.isTableModified = true;
                this.btnSave.Enabled = true;
                this.btnCancel.Enabled = true;
            }
        }

        private void TxtLabel_TextChanged(object? sender, EventArgs e)
        {
            if(this.lastGroup != null)
            {
                if(!this.isGroupModified)
                {
                    this.isGroupModified = true;
                }
                if(this.txtLabel.Text.Trim().Length < 1)
                {
                    this.txtLabel.Text = this.lastGroup.Name;
                }
            }
            else if(this.lastTable != null)
            {
                if(!this.isTableModified)
                {
                    this.isTableModified = true;
                }
                if(this.txtLabel.Text.Trim().Length < 1)
                {
                    this.txtLabel.Text = this.lastTable.Label;
                }
            }
            this.btnSave.Enabled = true;
            this.btnCancel.Enabled = true;
        }

        private void tvTables_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            if(e.Button != MouseButtons.Left)
            {
                return;
            }
            if(e.Node != this.lastNode)
            {
                var flag = this.BeforeTableChange() || this.BeforeGroupChange();
                if(flag)
                {
                    if(e.Node.Tag is TableInfo tbl)
                    {
                        this.lastGroup = null;
                        this.lastTable = tbl;
                        this.lastNode = e.Node;
                        this.ResetTableInfo(true);
                    }
                    else if(e.Node.Tag is GroupInfo grp)
                    {
                        this.lastGroup = grp;
                        this.lastTable = null;
                        this.lastNode = e.Node;
                        this.ResetGroupInfo();
                    }
                }
            }
        }

        private bool BeforeTableChange()
        {
            if(this.lastTable == null || !this.isTableModified)
            {
                return true;
            }

            var da = MessageBox.Show(this, "编辑的内容尚未保存，是否保存？", "提示", MessageBoxButtons.YesNoCancel);
            if(da == DialogResult.Yes)
            {
                this.SaveTableInfo();
                return true;
            }
            else if(da == DialogResult.No)
            {
                return true;
            }
            else
            {
                this.ResetTableInfo(false);
                return false;
            }
        }
        private bool BeforeGroupChange()
        {
            if(this.lastGroup == null || !this.isGroupModified)
            {
                return true;
            }

            var da = MessageBox.Show(this, "编辑的内容尚未保存，是否保存？", "提示", MessageBoxButtons.YesNoCancel);
            if(da == DialogResult.Yes)
            {
                this.SaveGroupInfo();
                return true;
            }
            else if(da == DialogResult.No)
            {
                return true;
            }
            else
            {
                this.isGroupModified = false;
                return false;
            }
        }

        private void SaveTableInfo()
        {
            if(!this.isTableModified)
            {
                return;
            }
            this.lastTable.Label = this.txtLabel.Text.Trim();
            this.lastTable.Description = this.txtDescription.Text.Trim();
            if(this.dgvColumns.DataSource is DataTable table)
            {
                table.AcceptChanges();
                this.lastTable.Columns = table.AsList<ColumnInfo>();
            }
            this.grpTable.Text = $"表：{this.lastTable.Name} 【{this.lastTable.Label}】";
            Application.DoEvents();

            this.Save();
            this.isTableModified = false;
            this.btnSave.Enabled = false;
            this.btnCancel.Enabled = false;

            Application.DoEvents();
        }
        private void SaveGroupInfo()
        {
            if(!this.isGroupModified)
            {
                return;
            }
            this.lastGroup.Name = this.txtLabel.Text.Trim();
            this.grpTable.Text = $"组：{this.lastGroup.Name}";
            this.lastNode.Text = $"{this.lastGroup.Name} ({this.lastGroup.Tables.Count})";
            Application.DoEvents();

            this.Save();
            this.isGroupModified = false;

            Application.DoEvents();
        }

        public void Save()
        {
            var jsonFileName = $"{MainForm.Project.Name}.json";
            var jsonText2 = JsonConvert.SerializeObject(MainForm.Groups, JsonHelper.JsonSerializerSettings);
            File.WriteAllText(jsonFileName, jsonText2, Encoding.UTF8);
        }

        private void ResetTableInfo(bool isInit)
        {
            this.grpTable.Text = $"表：{this.lastTable.Name} 【{this.lastTable.Label}】";
            if(isInit)
            {
                var tblClone = JsonHelper.Clone(this.lastTable);
                this.txtLabel.Text = tblClone.Label;
                this.txtDescription.Text = tblClone.Description;
                var table = tblClone.Columns.ToDataTable();
                table.AcceptChanges();
                this.dgvColumns.DataSource = table;
                this.dgvColumns.Refresh();
                this.pnlTableInfo.Visible = true;
                this.dgvColumns.Visible = true;
                this.lblTableInfo.Text = $"字段数：{tblClone.Columns.Count} 索引数：{tblClone.Indexes?.Count ?? 0}";
            }
            else
            {
                if(this.dgvColumns.DataSource is DataTable table)
                {
                    table.RejectChanges();
                }
                this.dgvColumns.Refresh();
            }
            this.isGroupModified = false;
            this.isTableModified = false;
            this.btnSave.Enabled = false;
            this.btnCancel.Enabled = false;
            this.btnAi.Enabled = true;
            this.btnPosition.Enabled = true;
            Application.DoEvents();
        }
        private void ResetGroupInfo()
        {
            this.txtLabel.Text = this.lastGroup.Name;
            this.pnlTableInfo.Visible = false;
            this.dgvColumns.Visible = false;
            this.grpTable.Text = $"组：{this.lastGroup.Name}";
            this.isTableModified = false;
            this.isGroupModified = false;
            this.btnSave.Enabled = false;
            this.btnCancel.Enabled = false;
            this.btnAi.Enabled = false;
            this.btnPosition.Enabled = false;
            Application.DoEvents();
        }

        private void BtnSave_Click(object? sender, EventArgs e)
        {
            btnSave.Enabled = false;
            if(this.lastTable != null)
            {
                this.SaveTableInfo();
            }
            else if(this.lastGroup != null)
            {
                this.SaveGroupInfo();
            }
        }

        private void BtnCancel_Click(object? sender, EventArgs e)
        {
            btnCancel.Enabled = false;
            if(this.lastTable != null)
            {
                this.ResetTableInfo(false);
            }
            else if(this.lastGroup != null)
            {
                this.ResetGroupInfo();
            }
        }

        private async void BtnNewGroup_Click(object? sender, EventArgs e)
        {
            var name = this.txtNewGroupName.Text.Trim();
            if(string.IsNullOrWhiteSpace(name))
            {
                MessageBox.Show(this, "请填写分组名称", "提示");
                this.txtNewGroupName.Focus();
                return;
            }
            if(MainForm.Groups.Any(x => string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase)))
            {
                MessageBox.Show(this, "分组名称已经存在，请重新填写", "提示");
                this.txtNewGroupName.SelectAll();
                this.txtNewGroupName.Focus();
                return;
            }

            var grp = new GroupInfo()
            {
                Name = name,
                Tables = new List<TableInfo>()
            };
            var node = new TreeNode(grp.Name) { Tag = grp };
            this.tvTables.Nodes.Add(node);
            MainForm.Groups.Add(grp);
            this.Save();
        }

        private async void BtnScript_Click(object? sender, EventArgs e)
        {
            this.btnScript.Enabled = false;
            var fileName = $"{MainForm.Project.Name}description_V{MainForm.Project.Version}.sql";
            var sfd = new SaveFileDialog
            {
                FileName = fileName,
                AddExtension = true,
                Filter = "数据库脚本(*.sql)|*.sql",
                Title = "指定要保存的文档"
            };
            if(sfd.ShowDialog() == DialogResult.OK)
            {
                var sqls = Executor.GetDescriptionSql(MainForm.Groups);
                File.WriteAllText(sfd.FileName, sqls.As<string>(), Encoding.UTF8);
                MainForm.Self.SetMessage("脚本导出成功");
                MessageBox.Show(this, "脚本导出成功", "提示");
            }
            await this.btnScript.Delay();
        }

        private async void BtnAi_Click(object? sender, EventArgs e)
        {
            btnAi.Enabled = false;

            if(this.lastTable == null)
            {
                MessageBox.Show(this, "请选择要分析的表格", "提示");
                btnAi.Enabled = false;
                return;
            }
            if(this.dgvColumns.DataSource is not DataTable table)
            {
                MessageBox.Show(this, "没有需要分析的数据", "提示");
                btnAi.Enabled = false;
                return;
            }
            var dataSource = table.AsList<ColumnInfo>();

            var cols = dataSource.Where(x => x.Label.IsEmptyOrLike(x.Name)).ToList();
            if(cols.Count < 1)
            {
                MessageBox.Show(this, "没有需要分析的数据", "提示");
                btnAi.Enabled = false;
                return;
            }

            var columns = MainForm.Groups.SelectMany(x => x.Tables)
                .Where(x => x.Name != this.lastTable.Name)
                .SelectMany(x => x.Columns)
                .Where(x => !x.Label.IsEmptyOrLike(x.Name))
                .ToList();
            if(columns.Count == 0)
            {
                MessageBox.Show(this, "目前没有可供学习的样本，或暂时没有进行过任何训练", "提示");
                btnAi.Enabled = false;
                return;
            }

            var dic = new Dictionary<string, string>();
            foreach(var col in cols)
            {
                var olds = columns.Where(x => x.Name.Equals(col.Name, StringComparison.OrdinalIgnoreCase)).ToList();
                if(olds.Any())
                {
                    var label = olds.GroupBy(x => x.Label.ToUpper()).OrderByDescending(x => x.Count()).First().Key;
                    dic.Add(col.Name, label);
                }
            }

            if(!dic.Any())
            {
                MessageBox.Show(this, "分析完成，未分析出任何结果，请扩展样本或进行更多相关训练", "提示");
                btnAi.Enabled = false;
                return;
            }
            var dialog = new FrmAiResult();
            //var str = dialog.FormatString(dic);
            dialog.SetContent(dic);
            var da = dialog.ShowDialog(this);
            //var info = $"分析出{dic.Count}条结果：\r\n{str}\r\n是否应用该结果？";
            //var da = MessageBox.Show(this, info, "提示", MessageBoxButtons.YesNo);
            if(da == DialogResult.Yes)
            {
                foreach(var col in cols)
                {
                    if(dic.TryGetValue(col.Name, out string? label))
                    {
                        col.Label = label;
                    }
                }
                this.dgvColumns.DataSource = dataSource.ToDataTable();
                this.dgvColumns.Refresh();
                this.isTableModified = true;
                this.btnSave.Enabled = true;
                this.btnCancel.Enabled = true;
                btnAi.Enabled = false;
            }
            else
            {
                await this.btnAi.Delay();
            }
        }

        private async void btnCreate_Click(object sender, EventArgs e)
        {
            this.btnCreate.Enabled = false;
            await this.Run();
            await this.btnCreate.Delay();
        }
        private async Task Run()
        {
            var xxdialog = new FrmInput();
            var dialogResukt = xxdialog.ShowDialog(this);

            if(dialogResukt == DialogResult.OK)
            {

            }
            else if(dialogResukt == DialogResult.Yes)
            {
                MainForm.Self.Save();
            }
            else
            {
                return;
            }

            NpoiWord.MessageCallback = MainForm.Self.SetMessage;

            var fileName = $"{MainForm.Project.Name}数据字典_V{MainForm.Project.Version}.docx";
            var sfd = new SaveFileDialog
            {
                FileName = fileName,
                AddExtension = true,
                Filter = "Word2007(*.docx)|*.docx",
                Title = "指定要保存的文档"
            };
            if(sfd.ShowDialog() == DialogResult.OK)
            {
                var logs = GetChanges();
                var rst = await NpoiWord.Export(sfd.FileName, MainForm.Project, MainForm.Groups, logs);
                if(rst.IsSuccess)
                {
                    MainForm.Self.SetMessage("文档导出成功");
                    var daopen = MessageBox.Show(this, "导出成功，是否打开文档", "提示", MessageBoxButtons.YesNo);
                    if(daopen == DialogResult.Yes)
                    {
                        var p = new Process();
                        p.StartInfo = new ProcessStartInfo(sfd.FileName)
                        {
                            UseShellExecute = true
                        };
                        p.Start();
                    }
                }
                else
                {
                    MainForm.Self.SetMessage("文档导出失败：" + rst.Message);
                    MessageBox.Show(this, "导出失败：\r\n" + rst.Message, "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }

        private List<ChangeLog> GetChanges()
        {
            var changes = new List<ChangeLog>();
            var jsonFileName = $"{MainForm.Project.Name}_changelogs.json";
            if(File.Exists(jsonFileName))
            {
                var jsonText = File.ReadAllText(jsonFileName);
                if(!string.IsNullOrWhiteSpace(jsonText))
                {
                    changes = JsonConvert.DeserializeObject<List<ChangeLog>>(jsonText);
                }
            }
            if(!string.IsNullOrWhiteSpace(MainForm.Project.ChangeLog))
            {
                changes.Add(new ChangeLog
                {
                    Date = DateTime.Now,
                    Description = MainForm.Project.ChangeLog,
                    Version = MainForm.Project.Version
                });
                var jsonText = JsonConvert.SerializeObject(changes);
                File.WriteAllText(jsonFileName, jsonText);
            }
            return changes;
        }
    }
}